package com.clementheliou.cheliou.web.controller.user.myaccount;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

import javax.faces.component.UIComponent;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.stereotype.Controller;

import com.clementheliou.cheliou.business.dataaccess.member.MemberDataAccessService;
import com.clementheliou.cheliou.business.technical.security.authentication.AuthenticationDetailsTechnicalService;
import com.clementheliou.cheliou.dal.model.member.Member;
import com.clementheliou.cheliou.dal.model.member.enumeration.MemberLocale;
import com.clementheliou.cheliou.web.controller.user.UserBean;
import com.clementheliou.cheliou.web.util.cookie.CookieUtils;
import com.clementheliou.cheliou.web.util.jsf.JSFUtils;

/**
 * Controller used to manage the "my account" view.
 * 
 * @author Clément HELIOU (clement.heliou@gmail.com)
 * @see {@link Controller};
 * @see {@link Scope};
 * @see {@link Serializable}.
 * @since 1.0
 */
@Controller
@Scope("session")
public class MyAccountBean implements Serializable {

	/**
	 * UID for serialization.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	private static final long serialVersionUID = 6418664291082741934L;

	/**
	 * This variable will cache the {@link MemberLocale} values as a
	 * {@link List} for the current session. Because updates on available
	 * languages can't be done without restart the application, we want retrieve
	 * the liste only once.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link MemberLocale};
	 * @see {@link List}.
	 * @since 1.0
	 */
	private List<MemberLocale> availableLanguages;

	/**
	 * This variable will cache the connected member data for the current
	 * session. This cache will be reset at the end of the HTTP session.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link Member}.
	 * @since 1.0
	 */
	private Member currentMember;

	/**
	 * Password encoder used to manage passwords of the session account.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link Md5PasswordEncoder};
	 * @see {@link PasswordEncoder}.
	 * @since 1.0
	 */
	private transient PasswordEncoder passwordEncoder = new Md5PasswordEncoder();

	/**
	 * Temporary variable used to store the uncrypted new password value.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	private String newPassword;

	/**
	 * Temporary variable used to store the uncrypted old password value.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	private String oldPassword;

	/**
	 * Utilities about cookies.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link Autowired};
	 * @see {@link CookieUtils}.
	 * @since 1.0
	 */
	@Autowired
	private transient CookieUtils cookieUtils;

	/**
	 * Utitlities about connected user data.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	@Autowired
	private transient AuthenticationDetailsTechnicalService authenticationDetailsService;

	/**
	 * Business service dedicated to the members management.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link Autowired};
	 * @see {@link Member}.
	 * @since 1.0
	 */
	@Autowired
	private transient MemberDataAccessService memberService;

	/**
	 * Utilities about <tt>JSF</tt>.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link Autowired};
	 * @see {@link JSFUtils}.
	 * @since 1.0
	 */
	@Autowired
	private transient JSFUtils jsfUtils;

	/**
	 * Controller dedicated to the user details and shared between sessions due
	 * to the use of Spring Security mecanism.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link Autowired};
	 * @see {@link UserBean}.
	 * @since 1.0
	 */
	@Autowired
	private transient UserBean userBean;

	/**
	 * Gets the {@link MemberLocale} values that can be used into the
	 * {@link UIComponent} as a {@link List} .
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the available locale values.
	 * @since 1.0
	 */
	public List<MemberLocale> getAvailableLanguages() {
		if (null == availableLanguages) {
			availableLanguages = Arrays.asList(MemberLocale.values());
		}
		return availableLanguages;
	}

	/**
	 * Gets the current member either from the business service if it's the 1st
	 * call, or from the cache if it's not.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the connected member data.
	 * @since 1.0
	 */
	public Member getCurrentMember() {
		if (null == currentMember) {
			currentMember = memberService.getMemberByEMail(userBean.getCurrentUser().getUsername());
		}
		return currentMember;
	}

	/**
	 * Sends the updated values to the business service if these ones are valid.
	 * Especially, it checks the consistency of the values related to password
	 * changes.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return <code>null</code> because we stay on the same page.
	 * @since 1.0
	 */
	public String updateMember() {
		if (null != newPassword && newPassword.length() != 0) {
			if (null == oldPassword || oldPassword.length() == 0) {
				jsfUtils.addErrorMessage("user_my_account_form_your_current_password_is_required_to_consider_the_new_one");
				return null;
			} else {
				if (!passwordEncoder.encodePassword(oldPassword, null).equals(currentMember.getPassword())) {
					jsfUtils.addErrorMessage("user_my_account_form_wrong_value_for_your_current_password");
					return null;
				} else {
					currentMember.setPassword(passwordEncoder.encodePassword(newPassword, null));
				}
			}
		}

		jsfUtils.addInfoMessage("user_my_account_form_your_account_has_been_successfully_updated");
		memberService.updateMember(currentMember);
		userBean.setFirstName(currentMember.getFirstName());
		userBean.setName(currentMember.getName());
		userBean.setMemberLocale(currentMember.getLocale());
		cookieUtils.createLocaleCookie(authenticationDetailsService.getCurrentUser(), jsfUtils.getExternalContextResponse());
		clear();

		return null;
	}

	/**
	 * Clears all the form values.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	private void clear() {
		newPassword = null;
		oldPassword = null;
	}

	/*
	 * Getters & setters.
	 */

	public String getNewPassword() {
		return newPassword;
	}

	public void setNewPassword(String newPassword) {
		this.newPassword = newPassword;
	}

	public String getoldPassword() {
		return oldPassword;
	}

	public void setOldPassword(String oldPassword) {
		this.oldPassword = oldPassword;
	}

	public void setCurrentMember(Member currentMember) {
		this.currentMember = currentMember;
	}
}
